//
// Created by wujehy on 2020/5/29.
//

#ifndef GEEJOAN_KEYVALUEDATATYPE_H
#define GEEJOAN_KEYVALUEDATATYPE_H


/**
 * @brief Key-Value 数据封装 用于 GeeJoan 服务端 缓存 数据 , 代替 Redis
 */

#include <functional>
#include <memory>
#include <cstring>

namespace GeeJoan
{
    namespace DataBase
    {

        typedef std::function<void(void *userdata)> UserPortDeleteFunction;
        enum ValueType {
            ValueType_UNDEFINED = 0, ///< 未定义
            ValueType_INT32 = 1, ///< 数字
            ValueType_INT64 = 2, ///< 数字
            ValueType_FLOAT = 3, ///< 小数
            ValueType_DOUBLE = 4,
            ValueType_BOOL = 5,
            ValueType_STRING = 6,
            ValueType_USERPORT = 7, ///< 指针
            ValueType_UInt8 = 8, ///< int8
        };

        struct ValueDataBaseType {
            uint8_t type = 0;
        };

        struct ValueDataBoolType : ValueDataBaseType {
            bool data;

        };

        struct ValueDataUInt8Type : ValueDataBaseType {
            uint8_t data;


        };

        struct ValueDataInt32Type : ValueDataBaseType {
            int data;

        };

        struct ValueDataInt64Type : ValueDataBaseType {
            int64_t data;
        };

        struct ValueDataFloatType : ValueDataBaseType {
            float data;
        };

        struct ValueDataDoubleType : ValueDataBaseType {
            double data;
        };


        struct ValueDataStringType : ValueDataBaseType {
            int len = 0;
            char *data;

            virtual ~ValueDataStringType()
            {
//                printf("delete string\n");

                if (this->data)
                {
                    delete (this->data);
                }
            };
        };


        struct ValueDataUserDataType : ValueDataBaseType {
            void *data;
            UserPortDeleteFunction deleteFunction;
            virtual ~ValueDataUserDataType()
            {
                if (deleteFunction)
                {
                    deleteFunction(data);
                }
            };
        };

        typedef std::shared_ptr<ValueDataBaseType> ValueDataBaseTypePort;

        class KeyValueDataType {
//        UserPortDeleteFunction deleteFunction = nullptr;
            ValueDataBaseTypePort valueDataPort;
        public:
            KeyValueDataType(bool status)
            {
                ValueDataBoolType *temp = new ValueDataBoolType;
                temp->type = ValueType_BOOL;
                temp->data = status;
                valueDataPort = std::shared_ptr<ValueDataBaseType>( temp);
            }

            KeyValueDataType(uint8_t number)
            {
                ValueDataUInt8Type *temp = new ValueDataUInt8Type;
                temp->type = ValueType_UInt8;
                temp->data = number;
                valueDataPort = std::shared_ptr<ValueDataBaseType>( temp);
            }

            KeyValueDataType(int number)
            {
                ValueDataInt32Type *temp = new ValueDataInt32Type;
                temp->type = ValueType_INT32;
                temp->data = number;
                valueDataPort = std::shared_ptr<ValueDataBaseType>( temp);
            }

            KeyValueDataType(int64_t number)
            {
                ValueDataInt64Type *temp = new ValueDataInt64Type;
                temp->type = ValueType_INT64;
                temp->data = number;
                valueDataPort = std::shared_ptr<ValueDataBaseType>( temp);
            }

            KeyValueDataType(float dec)
            {
                ValueDataFloatType *temp = new ValueDataFloatType;
                temp->type = ValueType_FLOAT;
                temp->data = dec;
                valueDataPort = std::shared_ptr<ValueDataBaseType>( temp);
            }

            KeyValueDataType(double dec)
            {
                ValueDataDoubleType *temp = new ValueDataDoubleType;
                temp->type = ValueType_DOUBLE;
                temp->data = dec;
                valueDataPort = std::shared_ptr<ValueDataBaseType>( temp);
            }

            KeyValueDataType(const std::string &str)
            {
                ValueDataStringType *temp = new ValueDataStringType;
                temp->len = str.length();
                temp->data = new char [temp->len];
                memcpy(temp->data , str.c_str() , temp->len);
                temp->type = ValueType_STRING;
                valueDataPort = std::shared_ptr<ValueDataBaseType>( temp);
            }


            KeyValueDataType(const char *str)
            {
                ValueDataStringType *temp = new ValueDataStringType;
                temp->len = strlen(str);
                char *strtemp =   new char [temp->len];
                memcpy(strtemp , str ,strlen(str));
                temp->data = strtemp;
                temp->type = ValueType_STRING;
                valueDataPort = std::shared_ptr<ValueDataBaseType>( temp);
            }

            KeyValueDataType(void *userdata, UserPortDeleteFunction deleteFunction)
            {
                ValueDataUserDataType *temp = new ValueDataUserDataType;
                temp->type = ValueType_USERPORT;
                temp->data = userdata;
                temp->deleteFunction = deleteFunction;
                valueDataPort = std::shared_ptr<ValueDataBaseType>( temp);
            }

            ValueType getType()  {
                return (ValueType)valueDataPort->type;
            }

            const ValueDataBaseTypePort &getDataPort() const
            {
                return valueDataPort;
            }

            const double getDouble() const
            {
                if (valueDataPort->type == ValueType_DOUBLE){
                    return  ((ValueDataDoubleType *) valueDataPort.get())->data;
                } else{
                    return 0;
                }
            }

            const float getFloat() const
            {
                if (valueDataPort->type == ValueType_FLOAT){
                    return  ((ValueDataFloatType *) valueDataPort.get())->data;
                } else{
                    return 0;
                }
            }
            const int getInt() const
            {
                if (valueDataPort->type == ValueType_INT32){
                    return  ((ValueDataInt32Type *) valueDataPort.get())->data;
                } else{
                    return 0;
                }
            }

            const int64_t getInt64() const
            {
                if (valueDataPort->type == ValueType_INT64){
                    return  ((ValueDataInt64Type *) valueDataPort.get())->data;
                } else{
                    return 0;
                }
            }


            const std::string getString() const
            {
                if (valueDataPort->type == ValueType_STRING){
                    return  ((ValueDataStringType *) valueDataPort.get())->data;
                } else{
                    return "";
                }
            }

            const uint8_t getUint8() const
            {
                if (valueDataPort->type == ValueType_UInt8){
                    return  ((ValueDataUInt8Type *) valueDataPort.get())->data;
                } else{
                    return 0;
                }
            }

            const bool getBool() const
            {
                if (valueDataPort->type == ValueType_BOOL){
                    return  ((ValueDataBoolType *) valueDataPort.get())->data;
                } else{
                    return false;
                }
            }
            const void* getAddr() const
            {
                if (valueDataPort->type == ValueType_USERPORT){
                    return  ((ValueDataUserDataType *) valueDataPort.get())->data;
                } else{
                    return 0;
                }
            }




            virtual std::string getValue(bool showType = false)
            {
                switch (valueDataPort->type)
                {
                    case ValueType_DOUBLE:
                    {
                        return (showType ? "(double) " : "") + std::to_string(((ValueDataDoubleType *) valueDataPort.get())->data);
                    }
                    case ValueType_FLOAT:
                    {
                        return (showType ? "(float) " : "") + std::to_string(((ValueDataFloatType *) valueDataPort.get())->data);
                        break;
                    }
                    case ValueType_STRING:
                    {
                        ValueDataStringType *portData = (ValueDataStringType *) valueDataPort.get();
                        return (showType ? "(string) " : "") + std::string(portData->data, portData->len);
                    }

                    case ValueType_INT32 :
                    {
                        return (showType ? "(int32) " : "") + std::to_string(((ValueDataInt32Type *) valueDataPort.get())->data);
                    }

                    case ValueType_INT64 :
                    {
                        return (showType ? "(int64) " : "") + std::to_string(((ValueDataInt64Type *) valueDataPort.get())->data);
                    }


                    case ValueType_BOOL :
                    {
                        return (showType ? "(bool) " : "") + ((ValueDataBoolType *) valueDataPort.get())->data ? "True" : "False";
                    }
                    case ValueType_USERPORT :
                    {
                        return (showType ? "(addr) " : "") +
                               std::to_string((int64_t) ((ValueDataUserDataType *) valueDataPort.get())->data);
                    }
                }
            }

        };


    }
}

#endif //GEEJOAN_KEYVALUEDATATYPE_H

